package org.springframework.social.dao;


import java.io.Serializable;
import java.util.Collection;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.DetachedCriteria;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;

public abstract class DaoHibernateImpl<T, ID extends Serializable> implements Dao<T, ID>
{

	/** Logger for this class and subclasses */
	protected final Log log = LogFactory.getLog(getClass());

	@Autowired
	protected SessionFactory sessionFactory;

	private Class<T> type;

	/**
	 * @param type
	 *            class
	 */
	public DaoHibernateImpl(Class<T> type)
	{
		this.type = type;
	}

	/**
	 * {@inheritDoc}
	 */
	public void delete(T entity)
	{
		sessionFactory.getCurrentSession().delete(entity);
	}

	/**
	 * {@inheritDoc}
	 */
	public List findObjectByCriteria(DetachedCriteria criteria)
	{
		List results = criteria.getExecutableCriteria(sessionFactory.getCurrentSession()).list();
		return results;
	}

	/**
	 * {@inheritDoc}
	 */
	@SuppressWarnings("unchecked")
	public List<T> findByCriteria(DetachedCriteria criteria)
	{
		List<T> results = criteria.getExecutableCriteria(sessionFactory.getCurrentSession()).list();

		return results;
	}

	public List<T> findByCriteria(DetachedCriteria criteria, int firstResult, int maxResults)
	{
		Criteria executableCriteria = criteria.getExecutableCriteria(sessionFactory.getCurrentSession());
		if (firstResult >= 0)
		{
			executableCriteria.setFirstResult(firstResult);
		}
		if (maxResults > 0)
		{
			executableCriteria.setMaxResults(maxResults);
		}
		List<T> results = executableCriteria.list();
		;
		return results;
	}

	/**
	 * {@inheritDoc}
	 */
	/*
	 * @SuppressWarnings("unchecked") public List<T> findByExample(T entity) {
	 * return getHibernateTemplate().findByExample(entity); }
	 */

	/**
	 * {@inheritDoc}
	 */
	@SuppressWarnings("unchecked")
	public T findById(ID id)
	{
		return (T) sessionFactory.getCurrentSession().get(type, id);
	}

	/**
	 * 
	 * {@inheritDoc}
	 */
	public T merge(T entity)
	{
		sessionFactory.getCurrentSession().merge(entity);
		return entity;
	}

	/**
	 * {@inheritDoc}
	 */
	public T save(T entity)
	{
		sessionFactory.getCurrentSession().saveOrUpdate(entity);
		return entity;
	}

	/**
	 * {@inheritDoc}
	 */
	public T load(Class c, Serializable id) throws DataAccessException
	{
		return (T) sessionFactory.getCurrentSession().load(c, id);

	}

	public void evict(T entity)
	{
		sessionFactory.getCurrentSession().evict(entity);
		sessionFactory.getCurrentSession().flush();
	}

	/**
	 * {@inheritDoc}
	 */
	public T update(T entity)
	{
		sessionFactory.getCurrentSession().update(entity);
		sessionFactory.getCurrentSession().flush();
		return entity;
	}

	public int bulkUpdate(String queryString, Object value)
	{
		Query queryObject = sessionFactory.getCurrentSession().createQuery(queryString);
		queryObject.setParameter(0, value);
		return Integer.valueOf(queryObject.executeUpdate());
	}

	public int bulkUpdate(String queryString, Object[] values)
	{
		Query queryObject = sessionFactory.getCurrentSession().createQuery(queryString);
		if (values != null)
		{
			for (int i = 0; i < values.length; ++i)
			{
				queryObject.setParameter(i, values[i]);
			}
		}
		return Integer.valueOf(queryObject.executeUpdate());
	}

	public int bulkUpdateByNamedParam(String queryString, String[] paramNames, Object[] values) throws DataAccessException
	{
		Query queryObject = sessionFactory.getCurrentSession().createQuery(queryString);
		if (values != null)
		{
			for (int i = 0; i < values.length; i++)
			{
				applyNamedParameterToQuery(queryObject, paramNames[i], values[i]);
			}
		}
		return new Integer(queryObject.executeUpdate());
	}

	protected void applyNamedParameterToQuery(Query queryObject, String paramName, Object value) throws HibernateException
	{
		if (value instanceof Collection)
		{
			queryObject.setParameterList(paramName, (Collection) value);
		}
		else if (value instanceof Object[])
		{
			queryObject.setParameterList(paramName, (Object[]) value);
		}
		else
			queryObject.setParameter(paramName, value);
	}

	public int bulkUpdate(String queryString)
	{
		return bulkUpdate(queryString, null);
	}

	public void persist(Object entity) throws DataAccessException
	{
		sessionFactory.getCurrentSession().persist(entity);
	}

	public List find(String query)
	{
		return sessionFactory.getCurrentSession().createQuery(query).list();
	}

	public List<T> find(String query, int firstResult, int maxResults)
	{
		Query queryObject = sessionFactory.getCurrentSession().createQuery(query);
		if (firstResult >= 0)
		{
			queryObject.setFirstResult(firstResult);
		}
		if (maxResults > 0)
		{
			queryObject.setMaxResults(maxResults);
		}
		List<T> results = queryObject.list();
		return results;
	}

	@SuppressWarnings("unchecked")
	public List<T> findByNamedParam(String queryString, String paramNames[], Object values[])
	{
		Query queryObject = sessionFactory.getCurrentSession().createQuery(queryString);
		if (values != null)
		{
			for (int i = 0; i < values.length; ++i)
			{
				applyNamedParameterToQuery(queryObject, paramNames[i], values[i]);
			}
		}
		List<T> results = queryObject.list();
		return results;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.price.domain.dao.Dao#findByNamedParam(java.lang.String,
	 * java.lang.String, java.lang.Object)
	 */
	@SuppressWarnings("unchecked")
	public List<T> findByNamedParam(String queryString, String paramName, Object value)
	{
		return findByNamedParam(queryString, new String[] { paramName }, new Object[] { value });
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.price.domain.dao.Dao#findByNamedParam(java.lang.String,
	 * java.lang.String, java.lang.Object, int, int)
	 */
	@SuppressWarnings("unchecked")
	public List<T> findByNamedParam(final String queryString, final String paramName, final Object value, final int firstResult, final int maxResults)
	{
		return findByNamedParam(queryString, new String[] { paramName }, new Object[] { value }, firstResult, maxResults);
	}

	@SuppressWarnings("unchecked")
	public List<T> findByNamedParam(String queryString, String paramNames[], Object values[], final int firstResult, final int maxResults)
	{
		if (paramNames.length != values.length)
		{
			throw new IllegalArgumentException("Length of paramNames array must match length of values array");
		}

		Query queryObject = sessionFactory.getCurrentSession().createQuery(queryString);
		if (values != null)
		{
			for (int i = 0; i < values.length; i++)
			{
				applyNamedParameterToQuery(queryObject, paramNames[i], values[i]);
			}
		}
		queryObject.setFirstResult(firstResult);
		if (maxResults != 0)
		{
			queryObject.setMaxResults(maxResults);
		}
		return queryObject.list();
	}

	@SuppressWarnings("unchecked")
	public List findObjectByNamedParam(String queryString, String paramName, Object value)
	{
		return findObjectByNamedParam(queryString, new String[] { paramName }, new Object[] { value });

	}

	@SuppressWarnings("unchecked")
	public List findObjectByNamedParam(String queryString, String paramName, Object value, final int firstResult, final int maxResults)
	{
		return findObjectByNamedParam(queryString, new String[] { paramName }, new Object[] { value }, firstResult, maxResults);

	}

	@SuppressWarnings("unchecked")
	public List findObjectByNamedParam(String queryString, String paramNames[], Object values[])
	{
		Query queryObject = sessionFactory.getCurrentSession().createQuery(queryString);
		if (values != null)
		{
			for (int i = 0; i < values.length; ++i)
			{
				applyNamedParameterToQuery(queryObject, paramNames[i], values[i]);
			}
		}
		List results = queryObject.list();
		return results;
	}

	@SuppressWarnings("unchecked")
	public List findObjectByNamedParam(String queryString, String paramNames[], Object values[], final int firstResult, final int maxResults)
	{
		Query queryObject = sessionFactory.getCurrentSession().createQuery(queryString);
		if (values != null)
		{
			for (int i = 0; i < values.length; ++i)
			{
				applyNamedParameterToQuery(queryObject, paramNames[i], values[i]);
			}
		}
		queryObject.setFirstResult(firstResult);
		if (maxResults != 0)
		{
			queryObject.setMaxResults(maxResults);
		}
		List results = queryObject.list();
		return results;
	}

	public void enabledFilter(String filterName, String paramName, Object value)
	{
		sessionFactory.getCurrentSession().enableFilter(filterName).setParameter(paramName, value);
	}

	public void enabledFilter(String filterName)
	{
		sessionFactory.getCurrentSession().enableFilter(filterName);
	}

	public Integer bulkExecuteSql(String queryString)
	{
		return sessionFactory.getCurrentSession().createSQLQuery(queryString).executeUpdate();
	}
}
